package com.qing.common.utils.sm;

import cn.hutool.core.util.ByteUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.BCUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;

import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Arrays;

public class Sm2Utils {

    public static void main(String[] args) {
//        sm2EncryptCustomKey();
/*        sm2Encrypt();

        sm2SignCustom();*/
//        sm2Sign();


        testAppServer();
    }

    private static void testAppServer() {
        String encText = "04b7cd296060803a9e55e0f997238899a8c9746955ce4e16cdc6616323dcce0407a66dffac7cbfcb1425a742a17adeb0f2f7f968d31eeb69562e037a799ee3a130ff68e3eed36c34e360366fff09e024fbd823908187313e5ded823e52849b382e52fc1a36b7a432d613e876fc479781671beab51498133c067c5a6ea299f2067044732570df6ec93b5a1bef92696bb9ec377958878feb7e37ddb7c8eca983468dc56fa5da65a6832a387613b934d9fec2b319edbab6a61861c26dbefe6ec04d2de82999d093db53db076101010997181284868e8e88c4b5750b214eb52c00bb0bb0b1279274cd60779668b680d043";
        String priKey = "64cb2ea5725919a8697487ffabf2298fc4dee2903a7f80b80ee4f6c160b5f519";

        String source = "{\"deviceId\":\"21E253527EB346BD906F10782D93DED9\",\"bizPort\":9003,\"knockToken\":\"97a6907c8487c4076a162ad0d4cdcb1067948d295013612e4b01fbab7320ac8b\"}";

        byte[] bytes = doSm2DecryptBytes(encText, priKey);
        String bytesString = Arrays.toString(bytes);
        System.out.println("解密的byte数组: ");
        System.out.println(bytesString);
        System.out.println("Source的byte数组");
        System.out.println(Arrays.toString(source.getBytes(StandardCharsets.UTF_8)));
        System.out.println("解密的字符串: ");
        System.out.println(new String(bytes, StandardCharsets.UTF_8));
    }

    private static void sm2Encrypt() {
        // 32 -> 129
        // 64 -> 161
        String source = "0123456789abcdeffedcba98765432100123456789abcdeffedcba9876543210";
        String source1 = "0123456789abcdeffedcba98765432100123456789abc";
//        String source1 = "0123456789abcdeffedcba9876543210";
        // 创建SM2加解密工具
        SM2 sm2 = new SM2();
        byte[][] pair = getKeyPair();
        // 获取公私钥的二进制字节数组
        byte[] priKeyEncoded = pair[0];
        byte[] pubKeyEncoded = pair[1];
        System.out.println("私钥长度: " + priKeyEncoded.length);
        System.out.println("公钥长度：" + pubKeyEncoded.length);
        // 获取公私钥的Base64
        String priKey = sm2.getPrivateKeyBase64();
        String pubKey = sm2.getPublicKeyBase64();
        System.out.println("私钥Base: " + priKey);
        System.out.println("公钥Base：" + pubKey);
        String enc = doSm2Encrypt(source, pubKeyEncoded);
        System.out.println("source原文长度： " + source.getBytes(StandardCharsets.UTF_8).length + ", 密文长度：" + enc.length()/2);
        String enc1 = doSm2Encrypt(source1, pubKeyEncoded);
        System.out.println("source1原文长度： " + source1.getBytes(StandardCharsets.UTF_8).length + ", 密文长度：" + enc1.length()/2);



        /*
         * 解密
         */
        String dec = doSm2Decrypt(enc, priKeyEncoded);
        System.out.println("密文：" + enc);
        System.out.println("解密后：" + dec);
        String dec1 = doSm2Decrypt(enc1, priKeyEncoded);
        System.out.println("密文：" + enc1 );
        System.out.println("解密后：" + dec1);


    }


    private static void sm2Sign() {
        String source = "11111111111111";
        String signed = doSm2Sign(source);

        System.out.println("SM2签名: ");
        System.out.println("原文： " + source );
        System.out.println("签名：" + signed);

        source = "0123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba9876543210";

        signed = doSm2Sign(source);
        System.out.println("SM2签名: ");
        System.out.println("原文： " + source );
        System.out.println("签名：" + signed);
    }

    private static void sm2SignCustom() {

        byte[][] pair = getKeyPair();
        // 获取公私钥的二进制字节数组
        byte[] priKeyEncoded = pair[0];
        byte[] pubKeyEncoded = pair[1];
        String source = "11111111111111";
        String signed = doSm2Sign(source, priKeyEncoded);

        System.out.println("SM2签名: ");
        System.out.println("原文： " + source);
        System.out.println("签名：" + signed);
        boolean verified = doSm2VerifySign(source, signed, pubKeyEncoded);
        System.out.println("验签通过? " + (verified));

        source = "0123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba9876543210";

        signed = doSm2Sign(source, priKeyEncoded);
        System.out.println("SM2签名: ");
        System.out.println("原文： " + source);
        System.out.println("签名：" + signed);
        verified = doSm2VerifySign(source, signed, pubKeyEncoded);
        System.out.println("验签通过? " + (verified));
    }

    private static byte[][] getKeyPair() {
        SM2 sm2 = new SM2();
        // 获取公私钥的二进制字节数组
        byte[] priKeyEncoded = sm2.getD();
        // 此时得到的公钥是65个字节，第一个字节是0x04，用来区分未压缩公钥
        byte[] pubKeyEncoded = sm2.getQ(false);

        byte[][] pair = new byte[2][];
        pair[0] = priKeyEncoded;
        pair[1] = pubKeyEncoded;
        return pair;
    }


    private static byte[][] getKeyPairPkcs8() {
        SM2 sm2 = new SM2();
        // 获取公私钥的二进制字节数组
        byte[] priKeyEncoded = sm2.getPrivateKey().getEncoded();
        byte[] pubKeyEncoded = sm2.getPublicKey().getEncoded();

        byte[][] pair = new byte[2][];
        pair[0] = priKeyEncoded;
        pair[1] = pubKeyEncoded;
        return pair;
    }

    private static String doSm2Sign(String source) {
        byte[] sign = SmUtil.sm2().sign(source.getBytes(StandardCharsets.UTF_8));
        return HexUtil.encodeHexStr(sign);
    }


    private static String doSm2Sign(String source, byte[] priKey) {
        byte[] srcBytes = source.getBytes(StandardCharsets.UTF_8);
        byte[] sign = doSm2Sign(srcBytes, priKey);
        return HexUtil.encodeHexStr(sign);
    }

    private static byte[] doSm2Sign(byte[] source, byte[] priKey) {
        // 使用私钥创建SM2签名工具
        SM2 sm2 = new SM2(priKey, null);

        // 对原始报文进行签名
        byte[] sign = sm2.sign(source);
        return sign;
    }

    private static boolean doSm2VerifySign(String source, String sign, byte[] pubKey) {
        byte[] srcBytes = source.getBytes(StandardCharsets.UTF_8);
        byte[] signBytes = HexUtil.decodeHex(sign);
        boolean verified = doSm2VerifySign(pubKey, srcBytes, signBytes);
        return verified;
    }

    private static boolean doSm2VerifySign(byte[] pubKey, byte[] source, byte[] signBytes) {
        // 使用公钥创建SM2签名工具
        SM2 sm2 = new SM2(null, pubKey);
        // 使用原始报文对签名进行验签操作
        boolean verified = sm2.verify(source, signBytes);
        return verified;
    }

    private static String doSm2Encrypt(String source, byte[] pubKey) {
        SM2 sm2 = new SM2(null, pubKey);
        return sm2.encryptHex(source.getBytes(StandardCharsets.UTF_8), KeyType.PublicKey);
    }

    private static String doSm2Encrypt(byte[] source, byte[] pubKey) {
        // 使用公钥创建SM2加密工具
        SM2 sm2 = new SM2(null, pubKey);
        // 使用公钥加密
        byte[] encrypt = sm2.encrypt(source, KeyType.PublicKey);
        return HexUtil.encodeHexStr(encrypt);
    }

    private static String doSm2Decrypt(String cipher, byte[] priKey) {
        SM2 sm2 = new SM2(priKey, null);
        return sm2.decryptStr(cipher, KeyType.PrivateKey);
    }

    private static byte[] doSm2DecryptBytes(String cipher, String priKeyHex) {
        SM2 sm2 = new SM2(HexUtil.decodeHex(priKeyHex), null);
        return sm2.decrypt(cipher, KeyType.PrivateKey);
    }



    private static String doSm2Decrypt(byte[] cipher, byte[] priKey) {
        // 使用私钥进行创建SM2解密工具
        SM2 sm2 = new SM2(priKey, null);
        // 使用私钥解密
        byte[] decrypt = sm2.decrypt(cipher, KeyType.PrivateKey);
        return HexUtil.encodeHexStr(decrypt);
    }



    private static void sm2EncryptCustomKey() {
        String source = "0123456789abcdeffedcba9876543210";

        // 生成SM2密钥对
        KeyPair keyPair = SecureUtil.generateKeyPair("SM2");
        PrivateKey aPriKey = keyPair.getPrivate();
        byte[] priKeyEncoded = aPriKey.getEncoded();
        PublicKey aPubKey = keyPair.getPublic();
        byte[] priK = BCUtil.encodeECPrivateKey(aPriKey);
        byte[] pubKeyEncoded = aPubKey.getEncoded();
        byte[] pubK = ((BCECPublicKey) aPubKey).getQ().getEncoded(true);
//        byte[] pubK = ((BCECPublicKey) aPubKey).getQ().getEncoded(false);
        SM2 sm2 = new SM2(priKeyEncoded, pubKeyEncoded);

        System.out.println("私钥长度: " + priK.length + ", 公钥长度：" + pubK.length);
        sm2.encrypt(source.getBytes(StandardCharsets.UTF_8));
        sm2.usePlainEncoding();
        sm2.setMode(SM2Engine.Mode.C1C2C3);

    }
    private static void sm2EncryptCustomKey1() {
        String source = "0123456789abcdeffedcba9876543210";

        // 生成SM2密钥对
        KeyPair keyPair = SecureUtil.generateKeyPair("SM2");
        // 私钥的二进制字节数组
        byte[] priKeyEncoded = keyPair.getPrivate().getEncoded();
        // 公钥的二进制字节数组
        byte[] pubKeyEncoded =  keyPair.getPrivate().getEncoded();

        // 使用自己生成的公私钥创建SM2加解密工具
        SM2 sm2 = new SM2(priKeyEncoded, pubKeyEncoded);

        sm2.encrypt(source.getBytes(StandardCharsets.UTF_8));
        sm2.usePlainEncoding();
        sm2.setMode(SM2Engine.Mode.C1C2C3);

    }
}
